package com.atguigu.tingshu.order.listener;

import com.atguigu.tingshu.album.client.TrackInfoFeignClient;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.base.BaseEntity;
import com.atguigu.tingshu.model.order.OrderDetail;
import com.atguigu.tingshu.model.order.OrderInfo;
import com.atguigu.tingshu.order.mapper.OrderDetailMapper;
import com.atguigu.tingshu.order.mapper.OrderInfoMapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.rabbitmq.client.Channel;
import lombok.SneakyThrows;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;


@Component
public class OrderTimeoutListener {

    /**
     * 下单15分钟以后:监听延迟消息取消超时未支付的订单
     */
    @RabbitListener(queues = "tingshu_order_dead_queue") // 监听死信队列中的消息
    @SneakyThrows
    public void orderTimeoutListenerTask(Channel channel, Message message){
        //获取消息的内容
        Long orderId = Long.valueOf(new String(message.getBody()));
        //获取消息的属性
        MessageProperties messageProperties = message.getMessageProperties();
        //获取消息的编号
        long deliveryTag = messageProperties.getDeliveryTag();
        try {
            //超时取消订单逻辑
            this.cancelTimeoutOrder(orderId);
            //确认消息
            channel.basicAck(deliveryTag, false);
        }catch (Exception e){
            if(messageProperties.getRedelivered()){
                //两次都失败
                channel.basicReject(deliveryTag, false);
            }else{
                //再取消一次
                channel.basicReject(deliveryTag, true);
            }
        }
    }

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private OrderInfoMapper orderInfoMapper;

    @Autowired
    private OrderDetailMapper orderDetailMapper;

    @Autowired
    private TrackInfoFeignClient trackInfoFeignClient;
    /**
     * 取消超时订单: 现在订单的状态!!!!!只取消未支付!!!!!(幂等性处理)
     * @param orderId
     */
    public void cancelTimeoutOrder(Long orderId) {
        //查询本次超时消息涉及的订单数据: 解决幂等性问题,使用订单的未支付状态进行校验!!!!
        OrderInfo orderInfo =
                orderInfoMapper.selectOne(
                        new LambdaQueryWrapper<OrderInfo>()
                                .eq(BaseEntity::getId, orderId)
                                .eq(OrderInfo::getOrderStatus, "0901"));//未支付的订单
        if(orderInfo == null){
            return;
        }
        //获取当前线程变量中的用户id
        Long userId = AuthContextHolder.getUserId();
        // 用户主动发起的取消订单请求 需要校验登录 会保存userId到当前的线程变量中
        if(userId == null){
            //不是人为取消 userId = null
            userId = orderInfo.getUserId();
            orderInfo.setOrderStatus("0904"); // 超时自动取消
        }else{
            orderInfo.setOrderStatus("0903"); // 用户主动取消
        }
        //并发问题: 每一笔订单的取消只操作数据库一次!!!
        RLock lock = redissonClient.getLock("Order_Cancel_" + orderId + "_User_" + userId);
        //快速失败机制!!
        if(lock.tryLock()){
            try {
                //写回数据库
                if(orderInfoMapper.updateById(orderInfo) < 0){
                    throw new GuiguException(201, "超时取消订单失败");
                }
            }catch (Exception e){
                throw e;
            }finally {
                lock.unlock();
            }
        }

        // 主动取消: 就删掉redis中下单时候加的15分钟过期的锁
        // 超时取消: redis中的锁的过期时间和订单超时时间是一致的 锁会自动消失
        if(orderInfo.getOrderStatus().equals("0903")){
            OrderDetail orderDetail =
                    orderDetailMapper.selectOne(
                            new LambdaQueryWrapper<OrderDetail>()
                                    .eq(OrderDetail::getOrderId, orderId)
                                    .last("limit 1"));
            Long itemId = orderDetail.getItemId();

            //声音
            if(orderInfo.getItemType().equals("1002")){
                //查询专辑id
                itemId = trackInfoFeignClient.getTrackInfo(itemId).getAlbumId();
            }
            redisTemplate.delete(userId + "_" + itemId);
        }
    }
}
